package com.gh.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.gh.bo.TransFlowBO;
import com.gh.common.exception.BusinessException;
import com.gh.common.result.ResponseEnum;
import com.gh.entity.*;
import com.gh.enums.*;
import com.gh.hfb.HfbConst;
import com.gh.hfb.RequestHelper;
import com.gh.mapper.*;
import com.gh.service.*;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gh.util.*;
import com.gh.vo.BorrowInfoApprovalVo;
import com.gh.vo.BorrowerDetailVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 标的准备表 服务实现类
 * </p>
 *
 * @author Eric
 * @since 2021-09-06
 */
@Slf4j
@Service
public class LendServiceImpl extends ServiceImpl<LendMapper, Lend> implements LendService {

    @Autowired
    private DictMapper dictMapper;

    @Autowired
    private BorrowerMapper borrowerMapper;

    @Autowired
    private BorrowerService borrowerService;

    @Autowired
    private UserAccountMapper userAccountMapper;

    @Autowired
    private UserInfoMapper userInfoMapper;

    @Autowired
    private TransFlowService transFlowService;

    @Autowired
    private LendItemService lendItemService;

    @Autowired
    private LendReturnService lendReturnService;

    @Autowired
    private LendItemReturnService lendItemReturnService;

    @Override
    public void createLend(BorrowInfoApprovalVo borrowInfoApprovalVo, BorrowInfo borrowInfo) throws ParseException {
        Lend lend = new Lend();
        lend.setUserId(borrowInfo.getUserId());
        lend.setBorrowInfoId(borrowInfo.getId());
        lend.setLendNo(LendNoUtils.getLendNo());//标的编号
        lend.setTitle(borrowInfoApprovalVo.getTitle());//标题
        lend.setAmount(borrowInfo.getAmount());//标的金额
        lend.setPeriod(borrowInfo.getPeriod());//投资期数
        lend.setLendYearRate(borrowInfo.getBorrowYearRate());//年化利率
        lend.setServiceRate(borrowInfoApprovalVo.getServiceRate().divide(new BigDecimal(100)));//平台服务费率
        lend.setReturnMethod(borrowInfo.getReturnMethod());//还款方式
        lend.setLowestAmount(new BigDecimal(100));//最低投资金额
        lend.setInvestAmount(new BigDecimal(0));//已投金额
        lend.setInvestNum(0);//投资人数
        lend.setPublishDate(new Date());//发布日期
        String lendStartDate = borrowInfoApprovalVo.getLendStartDate();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        Date startDate = sdf.parse(lendStartDate);
        lend.setLendStartDate(startDate);//起息日期
        LocalDate localDate = startDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
        LocalDate lendEndDate = localDate.plusMonths(borrowInfo.getPeriod());
        ZoneId zone = ZoneId.systemDefault();
        Instant instant = lendEndDate.atStartOfDay().atZone(zone).toInstant();
        Date endDate = Date.from(instant);
        lend.setLendEndDate(endDate);//结束日期
        lend.setLendInfo(borrowInfoApprovalVo.getLendInfo());//标的描述
        //平台预期收益率 年化/12*期数
        BigDecimal multiply = lend.getServiceRate().divide(new BigDecimal(12), 8, BigDecimal.ROUND_DOWN).multiply(new BigDecimal(lend.getPeriod()));
        BigDecimal expectAmount = lend.getAmount().multiply(multiply);
        lend.setExpectAmount(expectAmount);//预期收益
        lend.setRealAmount(new BigDecimal(0));//实际收益
        lend.setStatus(LendStatusEnum.INVEST_RUN.getStatus());//状态
        lend.setCheckTime(new Date());//审核时间
        lend.setCheckAdminId(1L);//审核人
        baseMapper.insert(lend);
    }

    @Override
    public List<Lend> selectList() {
        List<Lend> lendList = baseMapper.selectList(null);
        lendList.forEach(lend -> {
            String returnMethod = this.findDictByDictCodeAndValue(DictEnum.RETURN_METHOD.getDictCode(), lend.getReturnMethod());
            String status = LendStatusEnum.getMsgByStatus(lend.getStatus());
            lend.getParam().put("returnMethod", returnMethod);
            lend.getParam().put("status", status);
        });
        return lendList;
    }

    @Override
    public Map<String, Object> show(Long id) {
        Map<String, Object> map = new HashMap<>();
        Lend lend = baseMapper.selectById(id);
        String returnMethod = this.findDictByDictCodeAndValue(DictEnum.RETURN_METHOD.getDictCode(), lend.getReturnMethod());
        String status = LendStatusEnum.getMsgByStatus(lend.getStatus());
        lend.getParam().put("returnMethod", returnMethod);
        lend.getParam().put("status", status);
        //查询借款对象
        LambdaQueryWrapper<Borrower> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(Borrower::getUserId, lend.getUserId());
        Borrower borrower = borrowerMapper.selectOne(lambdaQueryWrapper);
        BorrowerDetailVo borrowerDetail = borrowerService.getBorrowerDetailVoById(borrower.getId());
        map.put("lend", lend);
        map.put("borrower", borrowerDetail);
        return map;
    }

    @Override
    public BigDecimal getInterestCount(BigDecimal invest, BigDecimal yearRate, Integer totalMonth, Integer returnMethod) {
        if (ReturnMethodEnum.ONE.getMethod().intValue() == returnMethod.intValue()) {
            BigDecimal interestCount = Amount1Helper.getInterestCount(invest, yearRate, totalMonth);
            return interestCount;
        }
        if (ReturnMethodEnum.TWO.getMethod().intValue() == returnMethod.intValue()) {
            BigDecimal interestCount = Amount2Helper.getInterestCount(invest, yearRate, totalMonth);
            return interestCount;
        }
        if (ReturnMethodEnum.THREE.getMethod().intValue() == returnMethod.intValue()) {
            BigDecimal interestCount = Amount3Helper.getInterestCount(invest, yearRate, totalMonth);
            return interestCount;
        }
        if (ReturnMethodEnum.FOUR.getMethod().intValue() == returnMethod.intValue()) {
            BigDecimal interestCount = Amount4Helper.getInterestCount(invest, yearRate, totalMonth);
            return interestCount;
        }
        return new BigDecimal(0);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void makeLoan(String id) {
        //获取标的信息
        Lend lend = baseMapper.selectById(id);
        //调用汇付宝放款接口
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("agentId", HfbConst.AGENT_ID);
        paramMap.put("agentProjectCode", lend.getLendNo());//放款项目编号
        paramMap.put("agentBillNo", LendNoUtils.getLoanNo());//放款编号
        //月年化
        BigDecimal monthRate = lend.getServiceRate().divide(new BigDecimal(12), 8, BigDecimal.ROUND_DOWN);
        //商户手续费 = 月年化*已投金额*投资时长
        BigDecimal realAmount = lend.getInvestAmount().multiply(monthRate).multiply(new BigDecimal(lend.getPeriod()));
        paramMap.put("mchFee", realAmount); //商户手续费
        paramMap.put("timestamp", RequestHelper.getTimestamp());
        String sign = RequestHelper.getSign(paramMap);
        paramMap.put("sign", sign);
        JSONObject result = RequestHelper.sendRequest(paramMap, HfbConst.MAKE_LOAD_URL);
        log.info("返回结果{}", result.toJSONString());
        //放款失败的处理
        if (!"0000".equals(result.getString("resultCode"))) {
            throw new BusinessException(result.getString("resultMsg"));
        }
//          （1）标的状态和标的平台收益
        lend.setRealAmount(realAmount);
        lend.setStatus(LendStatusEnum.PAY_RUN.getStatus());
        lend.setPaymentTime(new Date());
        baseMapper.updateById(lend);
//          （2）给借款账号转入金额  放款金额-平台手续费
        Long lendUserId = lend.getUserId();
        UserInfo userInfo = userInfoMapper.selectById(lendUserId);
        String bindCode = userInfo.getBindCode();
        userAccountMapper.updateAccount(bindCode, new BigDecimal(result.getString("voteAmt")), new BigDecimal(0));
//          （3）增加借款交易流水
        TransFlowBO transFlowBO = new TransFlowBO(
                result.getString("agentBillNo"),
                bindCode,
                new BigDecimal(result.getString("voteAmt")),
                TransTypeEnum.BORROW_BACK,
                "放款啦"
        );
        transFlowService.saveTransFlow(transFlowBO);
//          （4）解冻并扣除投资人资金
        List<LendItem> lendItemList = lendItemService.selectByLendId(Long.parseLong(id), 1);
        lendItemList.forEach(lendItem -> {
            Long investUserId = lendItem.getInvestUserId();
            UserInfo investUserInfo = userInfoMapper.selectById(investUserId);
            String investBindCode = investUserInfo.getBindCode();
            userAccountMapper.updateAccount(investBindCode, new BigDecimal(0), lendItem.getInvestAmount().negate());
//          （5）增加投资人交易流水
            TransFlowBO investTransFlowBo = new TransFlowBO(
                    LendNoUtils.getTransNo(),
                    investBindCode,
                    lendItem.getInvestAmount(),
                    TransTypeEnum.INVEST_UNLOCK,
                    "解冻啦"
            );
            transFlowService.saveTransFlow(investTransFlowBo);
        });
//          （6）生成借款人还款计划和出借人回款计划
        this.repaymentPlan(lend);
    }

    /**
     * 还款计划
     *
     * @param lend
     */
    private void repaymentPlan(Lend lend) {
        //假设分三期 10个人 ，还三期，每期的还款拆分成十份
        //把投资人应得的钱加起来，算出借款人的应还款的总金额
        //创建还款计划列表
        List<LendReturn> lendReturnList = new ArrayList<>();
        //按照还款时间生成还款计划
        Integer len = lend.getPeriod().intValue();
        for (Integer i = 1; i <= len; i++) {
            //创建还款计划对象
            LendReturn lendReturn = new LendReturn();
            lendReturn.setReturnNo(LendNoUtils.getReturnNo());
            lendReturn.setLendId(lend.getId());
            lendReturn.setBorrowInfoId(lend.getBorrowInfoId());
            lendReturn.setUserId(lend.getUserId());
            lendReturn.setAmount(lend.getAmount());//借款金额
            lendReturn.setBaseAmount(lend.getInvestAmount());//计息本金额
            lendReturn.setLendYearRate(lend.getLendYearRate());
            lendReturn.setCurrentPeriod(i);//当前期数
            lendReturn.setReturnMethod(lend.getReturnMethod());
            lendReturn.setFee(new BigDecimal(0));
            Date paymentTime = lend.getPaymentTime();//放款时间
            LocalDate localDate = paymentTime.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();//转化成localData类型
            LocalDate returnDates = localDate.plusMonths(i);
            ZoneId zone = ZoneId.systemDefault();
            Instant instant = returnDates.atStartOfDay().atZone(zone).toInstant();
            Date returnDate = Date.from(instant);
            lendReturn.setReturnDate(returnDate);//还款时指定的还款日期
            lendReturn.setOverdue(false);//是否预期
            //判断是否是最后一期
            if (len == i) {
                lendReturn.setLast(true);
            } else {
                lendReturn.setLast(false);
            }
            //设置还款状态
            lendReturn.setStatus(0);//0-未归还 1-已归还
            lendReturnList.add(lendReturn);
        }
        //批量保存还款计划
        lendReturnService.saveBatch(lendReturnList);
        //生成期数和还款记录的id对应的键值集合
        Map<Integer, Long> lendReturnMap = lendReturnList.stream().collect(Collectors.toMap(LendReturn::getCurrentPeriod, LendReturn::getId));
        //创建所有投资者回款计划列表
        List<LendItemReturn> lendItemReturnAllList = new ArrayList<>();
        //获取当前标的下的所有投资
        List<LendItem> lendItemList = lendItemService.selectByLendId(lend.getId(), 1);
        lendItemList.forEach(lendItem -> {
            List<LendItemReturn> lendItemReturnList = this.returnInvest(lendItem.getId(), lendReturnMap, lend);
            lendItemReturnAllList.addAll(lendItemReturnList);
        });
        //遍历还款记录列表
        lendReturnList.forEach(lendReturn -> {
            BigDecimal sumPrincipal = lendItemReturnAllList.stream()
                    .filter(lendItemReturn -> lendItemReturn.getLendReturnId().longValue() == lendReturn.getId().longValue())
                    .map(LendItemReturn::getPrincipal)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            BigDecimal sumInterest = lendItemReturnAllList.stream()
                    .filter(lendItemReturn -> lendItemReturn.getLendReturnId().longValue() == lendReturn.getId().longValue())
                    .map(LendItemReturn::getInterest)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            lendReturn.setPrincipal(sumPrincipal);
            lendReturn.setInterest(sumInterest);
            lendReturn.setTotal(lendReturn.getPrincipal().add(lendReturn.getInterest()));
        });
        lendReturnService.updateBatchById(lendReturnList);
    }

    /**
     * 回款计划
     *
     * @param lendItemId
     * @param lendReturnMap
     * @param lend
     * @return
     */
    public List<LendItemReturn> returnInvest(Long lendItemId, Map<Integer, Long> lendReturnMap, Lend lend) {
        //获取当前投资记录信息
        LendItem lendItem = lendItemService.getById(lendItemId);
        //调用工具类计算本金和利息，存储为集合
        BigDecimal investAmount = lendItem.getInvestAmount();
        BigDecimal lendYearRate = lendItem.getLendYearRate();
        Integer period = lend.getPeriod();
        Map<Integer, BigDecimal> mapInterest = null;//每月还款利息
        Map<Integer, BigDecimal> mapPrincipal = null;//每月还款本金
        if (lend.getReturnMethod().intValue() == ReturnMethodEnum.ONE.getMethod()) {
            mapInterest = Amount1Helper.getPerMonthInterest(investAmount, lendYearRate, period);
            mapPrincipal = Amount1Helper.getPerMonthPrincipal(investAmount, lendYearRate, period);
        } else if (lend.getReturnMethod().intValue() == ReturnMethodEnum.TWO.getMethod()) {
            mapInterest = Amount2Helper.getPerMonthInterest(investAmount, lendYearRate, period);
            mapPrincipal = Amount2Helper.getPerMonthPrincipal(investAmount, lendYearRate, period);
        } else if (lend.getReturnMethod().intValue() == ReturnMethodEnum.THREE.getMethod()) {
            mapInterest = Amount3Helper.getPerMonthInterest(investAmount, lendYearRate, period);
            mapPrincipal = Amount3Helper.getPerMonthPrincipal(investAmount, lendYearRate, period);
        } else {
            mapInterest = Amount4Helper.getPerMonthInterest(investAmount, lendYearRate, period);
            mapPrincipal = Amount4Helper.getPerMonthPrincipal(investAmount, lendYearRate, period);
        }
        //创建回款计划列表
        List<LendItemReturn> lendItemReturnList = new ArrayList<>();
        Set<Map.Entry<Integer, BigDecimal>> entries = mapInterest.entrySet();
        for (Map.Entry<Integer, BigDecimal> entry : entries) {
            Integer currentPeriod = entry.getKey();//当前期数
            Long lendReturnId = lendReturnMap.get(currentPeriod);//还款计划id
            LendItemReturn lendItemReturn = new LendItemReturn();//创建回款计划
            lendItemReturn.setLendReturnId(lendReturnId);
            lendItemReturn.setLendItemId(lendItemId);
            lendItemReturn.setInvestUserId(lendItem.getInvestUserId());
            lendItemReturn.setLendId(lend.getId());
            lendItemReturn.setInvestAmount(lendItem.getInvestAmount());
            lendItemReturn.setLendYearRate(lend.getLendYearRate());
            lendItemReturn.setCurrentPeriod(currentPeriod);
            lendItemReturn.setReturnMethod(lend.getReturnMethod());
            //设置回款本金、利息、和总额（注意最后一个月的计算）
            if (currentPeriod.intValue() == lend.getPeriod().intValue()) {
                //最后一期
                BigDecimal sumPrincipal = lendItemReturnList.stream()
                        .map(LendItemReturn::getPrincipal)
                        .reduce(BigDecimal.ZERO, BigDecimal::add);
                BigDecimal lastPrincipal = lendItem.getInvestAmount().subtract(sumPrincipal);
                lendItemReturn.setPrincipal(lastPrincipal);//最后一期本金
                BigDecimal sumInterest = lendItemReturnList.stream()
                        .map(LendItemReturn::getInterest)
                        .reduce(BigDecimal.ZERO, BigDecimal::add);
                BigDecimal lastInterest = lendItem.getExpectAmount().subtract(sumInterest);//最后一期的利息
                lendItemReturn.setInterest(lastInterest);
            } else {
                lendItemReturn.setPrincipal(mapPrincipal.get(currentPeriod));//本金
                lendItemReturn.setInterest(mapInterest.get(currentPeriod));//利息
            }
            Date paymentTime = lend.getPaymentTime();//放款时间
            LocalDate localDate = paymentTime.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();//转化成localData类型
            LocalDate returnDates = localDate.plusMonths(currentPeriod);
            ZoneId zone = ZoneId.systemDefault();
            Instant instant = returnDates.atStartOfDay().atZone(zone).toInstant();
            Date returnDate = Date.from(instant);
            lendItemReturn.setReturnDate(returnDate);//还款时指定的还款日期
            lendItemReturn.setTotal(lendItemReturn.getPrincipal().add(lendItemReturn.getInterest()));//本息
            lendItemReturn.setFee(new BigDecimal(0));
            lendItemReturn.setStatus(0);//0代表未还款
            lendItemReturnList.add(lendItemReturn);
        }
        lendItemReturnService.saveBatch(lendItemReturnList);

        return lendItemReturnList;
    }

    /**
     * 通过dictcode和value查询name
     *
     * @param dictCode
     * @param value
     * @return
     */
    private String findDictByDictCodeAndValue(String dictCode, Integer value) {
        LambdaQueryWrapper<Dict> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(Dict::getDictCode, dictCode);
        Dict parentDict = dictMapper.selectOne(lambdaQueryWrapper);
        if (parentDict == null) {
            return "";
        }
        lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(Dict::getParentId, parentDict.getId());
        lambdaQueryWrapper.eq(Dict::getValue, value);
        Dict dict = dictMapper.selectOne(lambdaQueryWrapper);
        if (dict == null) {
            return "";
        }
        return dict.getName();
    }
}
